/**
 * @class 行星系统实体
 * @author DuskyHuang 圣子
 * @description 中心行星和卫星系统
*/

import { _decorator, Node, instantiate, Camera, Vec3 } from 'cc';
import { Controller, core, utils } from 'cfw';
import { LAYER_MAIN_3D, Res, SceneGraph } from 'const';
import { View } from 'notifications';
import { CosmosProxy, planetary, visit, VisitProxy } from 'proxies';
import { Orbiter } from '../solar/orbiter';
import { Planet } from '../solar/planet';
import { Dsm } from '../solar/dsm';
import { createNodeAt } from 'helpers';
import { Belts } from '../solar/belts';
import { Magnetics } from '../gizmos/magnetics';
import { CometHightDefinition } from '../solar/comet-hd';
import { Astronomical } from '../gizmos/astronomical';
const { ccclass, property } = _decorator;

@ccclass('cocosmos.PlanetarySystem')
export class PlanetarySystem extends Controller {

    @property(Magnetics) public gmt: Magnetics = null;
    @property(Astronomical) public ast: Astronomical = null;
    @property(CometHightDefinition) public chd: CometHightDefinition = null;

    public get satellites(): Dsm[] { return this._satellites; }
    public getSatellite(name: satellite): Dsm { return this._satellites.find(sat => sat.node.name === name); }

    protected onLoad(): void { this._init()._listen(); }

    protected update(): void {
        const { showSatellites, showMagnetics } = planetary, { inPlanet, inComet } = visit;
        this._container.active = showSatellites && !this._hidingContents;
        this.gmt.node.active = inPlanet && showMagnetics && !this._hidingContents;
        this.ast.node.active = inPlanet && !this._hidingContents;
        this.chd.node.active = inComet;
    }

    private _init(): this {
        this._container = createNodeAt('Satellites', LAYER_MAIN_3D, this.node);
        this.gmt.node.active = this.ast.node.active = this.chd.node.active = false;
        return this;
    }
    private _listen(): void {
        this.on(Transform.FACE_OVER, () => this._hidingContents = false);
    }

    public loadSystem(): void {
        if (this._loaded) return console.warn('[planetary-system]', 'reload error!');
        const { transInfo: { target, name, mediator } } = visit;
        const to = mediator ?? target;
        const into = to.cat;
        planetary.reloadOrbiterParams(to as Orbiter, null, to.suggest + PLANET_SCOPE);
        const pluto = to.node.name === 'Pluto';
        if (into === 'planet' || pluto) {
            const planet = to as Planet;
            const pname = planet.node.name;
            const config = core.res.getConfigItem<planetConfig>(pluto ? SOLAR_DWARFS : SOLAR_PLANETS, pname);
            const satellites = config?.satellites;
            const radius = this._reloadSatellites(satellites, planet);
            planetary.reloadOrbiterParams(planet, satellites, radius);
            this.gmt.reloadConfig(planet.config);
            this.ast.reorientate(this.getSatellite('Moon'));
            this.ast.checkLoadTex();
        } else if (into === 'comet') {
            // TODO Comet Particles            
        } else if (into === 'belts') {
            console.log("Here into belts")
            const belts = to as Belts;
            belts.placeInPlanetary(name);
        }
        this._loaded = true;
    }

    private _reloadSatellites(satellites: satellite[], belongs: Planet | Dsm): pow10 {
        let farthest = 0;
        if (notEmptyArr(satellites)) {
            const viewer = utils.findCamera(COSMOS, MAIN);
            satellites.forEach(sat => {
                const config = core.res.getConfigItem<dsmConfig>(PLANETARY_SATELLITES, sat);
                const satellite = this._claim(config, viewer, belongs)
                this.satellites.push(satellite);
                const scope = satellite.orbitalRadius;
                if (scope > farthest) farthest = scope;
            });
        }
        this._hidingContents = true; // Here Planetary System changed
        return farthest === 0 ? 0 : Math.log10(farthest * 2) + BASE_POW10;
    }

    public freeSystem(): void {
        this._satellites.forEach(sat => this._free(sat));
        this._satellites.length = 0;
        this._loaded = false;
        planetary.reloadOrbiterParams(null);
        this.unscheduleAllCallbacks();
        // if (visit.transInfo.from instanceof Belts) {
        //     this.scheduleOnce(() => (visit.transInfo.from as Belts).placeInSolar(), 0.5);
        // }
        if (visit.transInfo.from instanceof Belts) {
            visit.transInfo.from.placeInSolar();
        }
    }

    private _claim(config: dsmConfig, viewer: Camera, belongs: Planet | Dsm): Dsm {
        const satellite = this._pool.pop() ?? instantiate(core.res.getPrefab(Prefabs.DSM)).getComponent(Dsm);
        satellite.setType('satellite');
        satellite.node.parent = this._container;
        satellite.loadConfig(config, viewer, BASE_POW10, belongs);
        return satellite;
    }
    private _free(satellite: Dsm): void {
        this._pool.push(satellite);
        satellite.node.removeFromParent();
    }

    private _satellites: Dsm[] = [];
    private _pool: Dsm[] = [];
    private _container: Node;
    private _hidingContents: boolean = false;
    private _loaded: boolean = false;
}

const { Prefabs, Configs: { SOLAR_PLANETS, SOLAR_DWARFS, PLANETARY_SATELLITES } } = Res;
const { COSMOS, Cameras: { MAIN } } = SceneGraph;
const { Transform } = View;
const BASE_POW10 = CosmosProxy.Scopes.Planetary[2];
const { LensOffsets: { PLANET_SCOPE } } = VisitProxy;